<?php
define('BR', "<br>\n");
/** 
 * @property User_order_mdl $order
 * @property Order_sync_queue_mdl $syncQueue
 * @property Order_cache_mdl $orderCache
 * @property User_mdl $users
 * @property Dev_mdl $dev
 * @property User_dev_mdl $userDev
 */
class Order_sync extends Bg_Controller {
	
	/**
	 */
	public function __construct() {
		parent::__construct ();
		
			$this->load->model('User_order_mdl','order');
		$this->load->model('Order_sync_queue_mdl','syncQueue');
		$this->load->model('Order_cache_mdl','orderCache');
		$this->load->model('User_mdl','users');
		$this->load->model('Dev_mdl','dev');
		$this->load->model('User_dev_mdl','userDev');
	}
	
	/**
	 * 开启循环任务
	 */
	public function cycle_task(){
		$this->load->database();
		$this->load->model('User_order_mdl','order');
		$this->load->model('Order_sync_queue_mdl','syncQueue');
		$this->load->model('Order_cache_mdl','orderCache');
		$this->load->model('User_mdl','users');
		$this->load->model('Dev_mdl','dev');
		$this->load->model('User_dev_mdl','userDev');
		
		$i = 1;
		while ( TRUE ) {
			// 从队列中获取待执行任务
			$task = $this->syncQueue->dequeue ();
			
			echo json_encode($task),BR;
			
			if (empty ( $task )) { // 无新任务时:暂停1秒
				$this->check_task();// 检查有没有其他任务
				
				// 清空队列
				$res = $this->syncQueue->clear();
				echo 'done:' . date ( 'Y-m-d H:i:s' ), BR;
				if (($i ++) % 5 === 4) {//每隔5秒检查一次有没有未同步的订单
					$this->checkUnSync (FALSE);
					$i = 0;
				}
				sleep ( 1 );
				continue;
			}
			// 获取订单
			$this->order->setUserID ( $task ['uid'] );
			if ( !empty($task['oid']) ) {
				$order = $this->order->getOneById ( $task ['oid'] );
			}else if( ! empty($task['mid']) ){
				$where = array('mid',$task['mid']);
				$order = $this->order->getOne($where);
			}
			
			
			
			if (empty ( $order )) {//获取不到该订单时,重新放进任务队列
				$this->syncQueue->enqueue($task['oid'],$task['uid'],$task['mid']);
				continue;
			}
			// 处理订单
			$res = $this->handleOrder($order);
		}
	}
	
	/**
	 * 处理订单
	 * @param array $order
	 */
	private function handleOrder($order,$isNew=TRUE){
		if( empty($order['amount']) || empty($order['isValid']) ){
			return NULL;
		}
// 		xdebug_start_code_coverage();
		
		$orderID = (int) $order['id'];
		$userID  = (int) $order['userID'];
		$devID   = (int) $order['devID'];
		
		$this->order->setUserID($userID);
		
		$dev   = $this->getDev($devID);
		$user  = $this->getUser($userID);
		$alias = $this->getAlias($userID,$devID);
		
		// 如果用户未设置学校,为其设置学校
		if( empty($user['schoolID']) && ! empty($dev['schoolID']) ){
			$update = array('schoolID'=>$dev['schoolID']);
			$this->users->updateOne('id',$userID, $update);
		}
		// 如果用户未添加设备,为其添加该设备
		if( empty($alias) && ! empty($dev['name']) ){
			$insert = array(
					'userID'=>$userID,
					'devID'=>$devID,
					'usedCount'=>1,
					'alias'=>$dev['name'],
			);
			$this->userDev->insert($insert);
		}else if($isNew){// 否则为用户使用该设备次数+1
			$where = array('userID'=>$userID,'devID'=>$devID);
			$this->userDev->increment($where, 'usedCount',1);
		}
		
		$order['nickname'] = $user['nickname'];
		$order['schoolID'] = $dev['schoolID'];
		$order['alias'] = $alias;
		
		$order = $this->formatOrder2cache($order);
		
		$res = $this->orderCache->save($order);
		
		if( $res ){
			$update = array('isCached'=>1);
			$where  = array('id'=>$orderID);
			$this->order->update($where, $update); 
		}
		query_debug();
		return $res;
	}
	
	
	
	private function formatOrder2cache($order,$isNew=TRUE){
		$doc = array ();
		// _id:1位表ID+8位订单ID
		$_id = ($order['userID']%10).sprintf("%08d",$order['id']);
		$doc['_id'] = (int)$_id;
		foreach ( $order as $k => $v ) {
			switch ($k) {
				case 'id':
				case 'devID' :
				case 'userID' :
				case 'isFinish' :
				case 'type' :
				case 'takeSec' :
				case 'price' :
				case 'amount' :
				case 'createTime' :
				case 'updateTime':
				case 'schoolID':
				case 'mode':
					$doc [$k] = ( int ) $v;
					break;
				case 'nickname':
				case 'alias':
					$doc[$k] = $v;
					break;
			}
		}
		
		if( $isNew ){
			$createTime = (int)$order['createTime'];
			if( empty($createTime) ){
				$createTime = $order['updateTime'] - $order['takeSec'];
			}
			if( $createTime <=0 ){
				$createTime = time();
			}
			$doc['date'] =(int) date('ymd',$createTime);
		}
		
		
		return $doc;
	}
	/**
	 * 用户列表
	 */
	private $userList = array();
	/**
	 * 设备别名列表
	 */
	private $userDevAliasList = array();
	/**
	 * 设备列表
	 */
	private $devList = array();
	/**
	 * 获取用户
	 * @param int $uid
	 * @return array
	 * 		'nickname'=>string,// 用户不存在时为空字符串
	 * 		'schoolID'=>int,
	 */
	private function getUser($uid){
		if( empty($this->userList[$uid]) ){
			$user = $this->users->getOneById($uid,'nickname,schoolID');
			if( empty($user) ){
				$user = array(
						'nickname'=>'',
						'schoolID'=>0,
				);
			}else{
				$user['schoolID'] = (int)$user['schoolID'];
			}
			$this->userList[$uid] = $user;
		}
		return $this->userList[$uid];
	}
	/**
	 * 获取用户的设备的别名
	 * @param int $uid
	 * @param int $devID
	 * @return string 未添加过时返回空字符串
	 */
	private function getAlias($uid,$devID){
		if( ! isset($this->userDevAliasList[$uid][$devID]) ){
			$userDev = $this->userDev->getUserDev($uid, $devID,'alias');
			if( empty($userDev) ){
				$dev = $this->getDev($devID);
				$alias = $dev['name'];
			}else{
				$alias = $userDev['alias'];
			}
			$this->userDevAliasList[$uid][$devID] = $alias;
		}
		return $this->userDevAliasList[$uid][$devID];
	}
	
	/**
	 * 获取用户及设备信息
	 * @param int $uid
	 * @param int $devID
	 * @return array
	 * 		'name'=>'RJ00010001',//找不到设备时为空字符串
	 * 		'schoolID'=>1
	 */
	private function getDev($devID){
		if( empty($this->devList[$devID]) ){
			$dev = $this->dev->getOneById($devID,'name,schoolID');
			if( empty($dev) ){
				$dev = array(
						'name'=>'',
						'schoolID'=>0,
				);
			}else{
				$dev['schoolID'] = (int)$dev['schoolID'];
			}
			$this->devList[$devID] = $dev;
		}
		return $this->devList[$devID];
	}
	/**
	 * 同步旧的订单到mongo
	 */
	public function syncOld(){
		$this->load->model('User_order_mdl','order');
		$this->load->model('Order_sync_queue_mdl','syncQueue');
		$this->load->model('Order_cache_mdl','orderCache');
		$this->load->model('User_mdl','users');
		$this->load->model('Dev_mdl','dev');
		$this->load->model('User_dev_mdl','userDev');
		
		$ts = microtime(true);
		
		
		$tableIdList = array(0,1,2,3,4,5,6,7,8,9);
// 		shuffle($tableIdList);
		foreach ( $tableIdList as $tableID ){
			$table = 'hs_order_'.$tableID;
			$limit = 1000;
					
			// 查出已缓存的最大Id
			$where = array();
			$where['_id'] = array();
			$where['_id']['$gte'] = (int) sprintf("%1d%08d",$tableID,0);//>=
			$where['_id']['$lte'] = (int) sprintf("%1d%08d",$tableID,99999999);//<=
			$maxID = $this->orderCache->max('id', $where);
// 			var_dump($maxID);
			
			do{
				$t = microtime(true);
				echo "{$table}\tmaxID:{$maxID}\t",$t-$ts,"\t",count($this->db->queries),BR;
				$ts = $t;
				
				$list = $this->db->select('*')
							->where('id >',$maxID)
							->where('createTime >',0)
							->order_by('id','ASC')
							->get($table,$limit)
							->result_array();
				if(empty($list)){
					break;
				}
				$listCount = count($list);
				$maxID = (int)$list[$listCount-1]['id'];
				
				$devIdList = array_column($list, 'devID');
				$userIdList = array_column($list, 'userID');
				
				$devIdList = array_unique($devIdList);
				$userIdList = array_unique($userIdList);
				
				$this->devList  = $this->dev->getListByFieldList('id,name,schoolID', 'id', $devIdList); 
				$this->userList = $this->users->getListByFieldList('id,nickname,schoolID', 'id', $userIdList); 
				
				$userDevList = $this->db->select('userID,devID,alias')
										->where_in('userID',$userIdList)
										->where_in('devID',$devIdList)
										->get(MY_Model::T_USER_DEV)
										->result_array();
				foreach ($userDevList as $v){
					$this->userDevAliasList[$v['userID']][$v['devID']] = $v['alias'];
				}
				
// 				pr($this->userList);die;
// 				var_dump($this->db->queries,$maxID,$list);die;
				foreach ($list as $order){
					$this->handleOrder($order,FALSE);
				}
// 				die;
			}while(count($list)>=$limit);
			
			
		}
	}
	
	/**
	 * 检查没有被同步的订单
	 * @param bool $needCheckAll 是否需要扫描全表(默认TRUE)
	 */
	public function checksomeSync($needCheckAll=TRUE){
	    $tableIdList = array(0,1,2,3,4,5,6,7,8,9);
	    shuffle($tableIdList);
	    $now_time=time();
	    $begin_time=strtotime(date('Y-m-d',$now_time-24*3600));//strtotime("2021-09-01");
	    $end_time=strtotime(date('Y-m-d',$now_time));//strtotime("2021-09-06");
	    //echo 1;die;
		foreach ($tableIdList as $tableID){
	        //sleep(1);
	        // 查询mysql中未同步的订单
	        $table = 'hs_order_'.$tableID;
	        $limit = 1000;
	        $select = '*';
	        $sql = "SELECT {$select} FROM {$table} WHERE ";
	        if( ! $needCheckAll ){
	            // 如果不需要扫描全表
	            // 获取最近5000条订单的最小ID,避免全表扫描
	            $sql.= " id >= (SELECT MAX(id) FROM {$table} )-50000 AND  ";
	        }
	        $sql.=" createTime>=".$begin_time." AND createTime<".$end_time." AND  isCached=1 AND isFinish=1 AND amount>0 AND isValid=1";
	        $sql.=" ORDER BY id DESC ";
	        $sql.=" LIMIT {$limit} ";
	        
	        $orders = $this->db->query($sql)->result_array();
	        query_debug();
	        if( empty($orders) ) continue;
	        
	        $orderIdList = array_map('intval', array_column($orders, 'id'));
	        $orders = array_combine($orderIdList, $orders);
	        
	        // 查询mongo已经同步过的订单
	        $maxID = (int)max($orderIdList);
	        $minID = (int)min($orderIdList);
	        $where = array(
	            '_id'=>array(
	                '$gte'=>(int) sprintf("%1d%08d",$tableID,$minID),
	                '$lte'=>(int) sprintf("%1d%08d",$tableID,$maxID),
	            ),
	            'id'=>array(
	                '$in'=>$orderIdList,
	            ),
	            'isFinish'=>1,
	        );
	        $fields = array('id'=>true);
	        $cachedOrders = $this->orderCache->getOrders($fields,$where);
	        // 缓存过的订单直接更新mysql
	//	$id_temp=array();      
	  foreach ($cachedOrders as $order){
	         //var_dump($order);die;
		//$id_temp[]=$order['id'];    
		$where = array('id'=>$order['id']);
	            //$this->db->set('isCached',1)->where($where)->update($table);
	            unset($orders[$order['id']]);
	        }
	        //var_dump(count($orders));die;
	        // 未缓存的先缓存,再更新mysql
	        foreach ($orders as $order){
	            $this->handleOrder($order,FALSE);
	        }
	        query_debug();
	        // 			die;
	    }
	    //sleep(1);$this->checkUnSync();
	    
	}

	/**
	 * 检查没有被同步的订单
	 * @param bool $needCheckAll 是否需要扫描全表(默认TRUE)
	 */
	public function checkUnSync($needCheckAll=TRUE){
		$tableIdList = array(0,1,2,3,4,5,6,7,8,9);
		shuffle($tableIdList);
		foreach ($tableIdList as $tableID){
			//sleep(1);
			// 查询mysql中未同步的订单
			$table = 'hs_order_'.$tableID;
			$limit = 1000;
			$select = '*';
			$sql = "SELECT {$select} FROM {$table} WHERE ";
			if( ! $needCheckAll ){
				// 如果不需要扫描全表
				// 获取最近5000条订单的最小ID,避免全表扫描
				$sql.= " id >= (SELECT MAX(id) FROM {$table} )-50000 AND  ";
			}
			$sql.=" isCached=0 AND isFinish=1 AND amount>0 AND isValid=1";
			$sql.=" ORDER BY id DESC ";
			$sql.=" LIMIT {$limit} ";
			
			$orders = $this->db->query($sql)->result_array();
			query_debug();
			if( empty($orders) ) continue;
			
			$orderIdList = array_map('intval', array_column($orders, 'id'));
			$orders = array_combine($orderIdList, $orders);
			
			// 查询mongo已经同步过的订单
			$maxID = (int)max($orderIdList);
			$minID = (int)min($orderIdList);
			$where = array(
					'_id'=>array(
							'$gte'=>(int) sprintf("%1d%08d",$tableID,$minID),
							'$lte'=>(int) sprintf("%1d%08d",$tableID,$maxID),
					),
					'id'=>array(
							'$in'=>$orderIdList,
					),
					'isFinish'=>1,
			);
			$fields = array('id'=>true);
			$cachedOrders = $this->orderCache->getOrders($fields,$where);
			// 缓存过的订单直接更新mysql
			foreach ($cachedOrders as $order){
				$where = array('id'=>$order['id']);
				$this->db->set('isCached',1)->where($where)->update($table);
				unset($orders[$order['id']]);
			}
			// 未缓存的先缓存,再更新mysql
			foreach ($orders as $order){
				$this->handleOrder($order,FALSE);
			}
			query_debug();
// 			die;
		}
		//sleep(1);$this->checkUnSync();
		
	}
	
	
// 	public function checkUnFinish(){
		
// 		$where = array();
// 		$where['isFinish'] = 0;
// 		$where['createTime']['$gte']=time()-86400*90;
// 		$fields = array('_id','id','userID');
// 		$orders = $this->orderCache->getOrders($fields,$where);
		
// 		$list = array();
// 		foreach ($orders as $v){
// 			$tableID = $v['userID']%10;
// 			$list[$tableID][] = $v['id'];
// 		}
// 		foreach ($list as $tableID=>$idList){
// 			$this->order->setUserID((int)$tableID);
// 			$orders = $this->order->getListByFieldList('*', 'id', $idList);
// 			foreach ($orders as $order){
// 				if ($order['isFinish']==0){
// 					continue;
// 				}
// 				$this->handleOrder($order,FALSE);
// 			}
// 		}
// 		var_dump($this->db->queries,$list);
// 	}
	
	
	public function test(){
		$this->load->database();
		$this->load->model('User_order_mdl','order');
		$this->load->model('Order_sync_queue_mdl','syncQueue');
		$this->load->model('Order_cache_mdl','orderCache');
		$this->load->model('User_mdl','users');
		$this->load->model('Dev_mdl','dev');
		$this->load->model('User_dev_mdl','userDev');
		
		$uid = 3532;
		$oid = 65805;
		$this->order->setUserID ( $uid );
		$order = $this->order->getOneById ( $oid );
		$res = $this->handleOrder($order);
	}
	
	
	function check_task(){
// 		$h = date('H');
// 		$taskFile = APPPATH.'models/task/task.php';
// 		echo "check_task({$h}):",file_exists($taskFile),BR;
// 		if(($h>3 && $h<5) && file_exists($taskFile) ){
// 			$cfg = include($taskFile);
// 			$this->load->model($cfg['model'],$cfg['as']);
// 			var_dump($cfg);
// 			$this->{$cfg['as']}->run();
// 			@unlink($taskFile);
// 		}
	}
	
// 	public function sharding(){
// 		$this->load->database();
// 		$tids = array(0,1,2,3,4,5,6,7,8,9);
// 		// 分表插入
// 		foreach ($tids as $tid){
// 			$where =" isFinish=1  AND isSharding=0 AND userID%10={$tid} ";
// 			$sql = "INSERT INTO `hs_order_{$tid}` 
// 					(`userID`, `isFinish`, `type`, `createTime`, `updateTime`, `takeSec`, `price`, `amount`, `phoneID`, `devID`) 
// 					(SELECT `userID`, `isFinish`, `type`, `createTime`, `finishTime`, `takeSec`, `price`, `amount`, `phoneID`, `devID` 
// 							FROM hs_order WHERE $where)";
// 			$res = $this->db->query($sql);
// 			if( ! $res ){
// 				continue;
// 			}
// 			$sql = "UPDATE `hs_order` SET `isSharding`=1 WHERE $where ";
// 			$this->db->query($sql);
// 		}
// 		var_dump($this->db->queries);
// 	}
	
	
	
	
	
	
}



function query_debug(){
// 	return true;
	echo BR;
	
	$CI = &get_instance();
	
	$queries = $CI->db->queries;
	$times = $CI->db->query_times;
	
	foreach($queries as $k=>$sql){
		
		$sql = str_replace("\r", ' ', $sql);
		$sql = str_replace("\n", ' ', $sql);
		echo $times[$k],'==',$sql,BR;
	}
	$CI->db->queries = array();
	$CI->db->query_times = array();
	
	
}



